using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Budgeteer.Services;
using Budgeteer.Services.Common;
using Budgeteer.Services.DataProviders;
using Budgeteer.Services.Domain;
using Test.UnitTests.Budgeteer.Services.DataProviders;
using Xunit;

namespace Test.UnitTests.Budgeteer.Services
{
    public class CategoryServiceFacts
    {
        public class ChartXmlDataGeneration
        {
            readonly IList<CategoryDto> categories;
            const string title = "Spending Categories";

            readonly CategoryService service;

            public ChartXmlDataGeneration()
            {
                ConfigService config = new ConfigService();
                CategoryDataProvider dataProvider = new CategoryDataProvider(Config.ConnectionString);
                service = new CategoryService(dataProvider,config);
                categories = new List<CategoryDto>
                    {
                        new CategoryDto { Name="HOME IMPROVEMENTS", TotalAmount=364.96M },
                        new CategoryDto { Name="COFFEE", TotalAmount=40.16M },
                        new CategoryDto { Name="EATING OUT", TotalAmount=147.68M }
                    };
            }

            [Fact]
            public void WillHaveRootChartElement()
            {
                string actual = service.GetChartXmlData(title,new List<CategoryDto>());

                XDocument doc = XDocument.Parse(actual);
                Assert.NotNull(doc);

                doc.Root.ReplaceNodes("");
                Assert.Equal(@"<vc:Chart xmlns:vc=""clr-namespace:Visifire.Charts;assembly=Visifire.Charts"" Theme=""Theme3"" AnimationType=""Type3""></vc:Chart>",
                    doc.Root.ToString());
            }

            [Fact]
            public void WillHaveTitleElement()
            {
                string actual = service.GetChartXmlData(title, categories);

                XDocument doc = XDocument.Parse(actual);
                XNamespace vc = doc.Root.GetNamespaceOfPrefix("vc");
                Assert.NotNull(vc);

                Assert.Equal(title,doc.Root.Element(vc + "Title").Attribute("Text").Value);
            }

            [Fact]
            public void WillHaveAxisTitleElements()
            {
                string actual = service.GetChartXmlData(title,categories);

                XDocument doc = XDocument.Parse(actual);
                XNamespace vc = doc.Root.GetNamespaceOfPrefix("vc");

                Assert.Equal("Categories", doc.Root.Element(vc + "AxisX").Attribute("Title").Value);
                Assert.Equal("Amount", doc.Root.Element(vc + "AxisY").Attribute("Title").Value);
            }

            [Fact]
            public void WillHaveDataSeriesElement()
            {
                string actual = service.GetChartXmlData(title,categories);

                XDocument doc = XDocument.Parse(actual);
                XNamespace vc = doc.Root.GetNamespaceOfPrefix("vc");

                Assert.Equal("Column",doc.Root.Element(vc + "DataSeries").Attribute("RenderAs").Value);
                Assert.Equal("True",doc.Root.Element(vc + "DataSeries").Attribute("LabelEnabled").Value);
            }

            [Fact]
            public void WillAddDataPointElementForEachCategory()
            {
                string actual = service.GetChartXmlData(title,categories);

                XDocument doc = XDocument.Parse(actual);
                XNamespace vc = doc.Root.GetNamespaceOfPrefix("vc");

                XElement parent = doc.Root.Element(vc + "DataSeries");

                Assert.Equal(categories.Count,parent.Elements(vc + "DataPoint").Count());
                Assert.Equal(categories[0].Name, parent.Elements(vc + "DataPoint").ToArray()[0].Attribute("AxisLabel").Value);
                Assert.Equal(categories[0].TotalAmount,Convert.ToDecimal(parent.Elements(vc + "DataPoint").ToArray()[0].Attribute("YValue").Value));
                Assert.Equal(categories[1].Name,parent.Elements(vc + "DataPoint").ToArray()[1].Attribute("AxisLabel").Value);
                Assert.Equal(categories[1].TotalAmount,Convert.ToDecimal(parent.Elements(vc + "DataPoint").ToArray()[1].Attribute("YValue").Value));
                Assert.Equal(categories[2].Name,parent.Elements(vc + "DataPoint").ToArray()[2].Attribute("AxisLabel").Value);
                Assert.Equal(categories[2].TotalAmount,Convert.ToDecimal(parent.Elements(vc + "DataPoint").ToArray()[2].Attribute("YValue").Value));
            }
        }

        public class CategoryClastering
        {
            readonly IDictionary<string,string> categoryMasks;

            readonly CategoryService service;

            public CategoryClastering()
            {
                ConfigService config = new ConfigService();
                CategoryDataProvider dataProvider = new CategoryDataProvider(Config.ConnectionString);
                service = new CategoryService(dataProvider,config);
                categoryMasks = new Dictionary<string,string>
                    {
                        {"COFFEE","STARBUCKS|COFFEE"},
                        {"HOME IMPROVEMENTS","HOME DEPOT"},
                        {"HOME EXPENSES","USPS|BEST BUY|GOODWILL"}
                    };
            }

            [Fact]
            public void WillBuildRegexFromCategoryMaskList()
            {
                string regex = service.BuildCategoryClasteringRegex(categoryMasks);

                Assert.Equal("(?<COFFEE>STARBUCKS|COFFEE)|(?<HOME_IMPROVEMENTS>HOME DEPOT)|(?<HOME_EXPENSES>USPS|BEST BUY|GOODWILL)",regex);
            }

            [Fact]
            public void WillDetectCategoryByChargeDescription()
            {
                string category = service.GetCategoryByChargeDescription(categoryMasks,"10/15/2004,THE HOME DEPOT 4706,8.50");

                Assert.Equal("HOME IMPROVEMENTS",category);
            }
        }

        public class GetCategoryAmount
        {
            [Fact]
            public void WillPassStartAndEndDateOfCurrentMonth()
            {
                ConfigService config = new ConfigService();
                StubCategoryDataProvider dataProvider = new StubCategoryDataProvider();
                StubCategoryService categoryService = new StubCategoryService(dataProvider,config);

                categoryService.IsDevMode_Result = false;

                using(Time.Freeze(new DateTime(2004,11,10,4,32,44)))
                {
                    categoryService.GetCategoriesAmount();

                    Assert.Equal(new DateTime(2004,11,1),dataProvider.GetCategoriesAmount_StartDate);
                    Assert.Equal(new DateTime(2004,11,30),dataProvider.GetCategoriesAmount_EndDate);
                }
            }

            [Fact]
            public void WillUseStartTimeFromConfigInDevMode()
            {
                ConfigService config = new ConfigService();
                StubCategoryDataProvider dataProvider = new StubCategoryDataProvider();
                StubCategoryService categoryService = new StubCategoryService(dataProvider,config);

                categoryService.IsDevMode_Result = true;

                using(Time.Freeze(new DateTime(2004,11,10,4,32,44)))
                {
                    categoryService.GetCategoriesAmount();

                    Assert.Equal(new DateTime(2004,9,1),dataProvider.GetCategoriesAmount_StartDate);
                    Assert.Equal(new DateTime(2004,9,30),dataProvider.GetCategoriesAmount_EndDate);
                }
            }
        }
    }

    public class StubCategoryService : CategoryService
    {
        public bool IsDevMode_Result;

        public StubCategoryService(CategoryDataProvider categoryDataProvider,ConfigService config)
            : base(categoryDataProvider,config)
        {}

        public override bool IsDevMode
        {
            get
            {
                return IsDevMode_Result;
            }
        }
    }
}